package com.xinxin.socket.context;

import com.google.common.collect.Maps;
import com.xinxin.socket.msg.NetMsg;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;

@Slf4j
public class ChannelLoginContext {

    private static final ChannelLoginContext INSTANCE = new ChannelLoginContext();

    private final ConcurrentMap<Channel, Long> channelLogin = Maps.newConcurrentMap();
    private final ConcurrentMap<Long, Channel> channelLoginTurn = Maps.newConcurrentMap();

    private ChannelLoginContext() {
    }

    public static ChannelLoginContext getInstance() {
        return INSTANCE;
    }

    public boolean isLogin(Channel ch) {
        return channelLogin.containsKey(ch);
    }

    public boolean isLogin(Long loginId) {
        return channelLoginTurn.containsKey(loginId);
    }

    public Long getLoginId(Channel ch) {
        return channelLogin.get(ch);
    }

    public Channel getChannel(Long loginId) {
        return channelLoginTurn.get(loginId);
    }

    public Set<Long> getLoginIdSet() {
        return channelLoginTurn.keySet();
    }

    public Set<Channel> geChannelSet() {
        return channelLogin.keySet();
    }

    public void joinToken(Channel ch, Long loginId) {
        // 当前连接重复登录
        if (isLogin(ch)) {
            log.info("Channel already login {}, loginId: {}, currentId: {}",
                    ch.id(), loginId, getLoginId(ch));

            removeToken(ch);
        }

        // 顶号登录
        if (isLogin(loginId)) {
            Channel ch0 = getChannel(loginId);

            InetSocketAddress remoteAddress = (InetSocketAddress) ch0.remoteAddress();
            String clientIp = remoteAddress.getAddress().getHostAddress();
            int clientPort = remoteAddress.getPort();
            log.info("Login id exist: {}. Channel id: {}, ip: {}, port: {}",
                    loginId, ch.id(), clientIp, clientPort);

            removeToken(ch0);
        }

        log.info("Channel login {}, loginId: {}", ch.id(), loginId);
        channelLogin.put(ch, loginId);
        channelLoginTurn.put(loginId, ch);
    }

    public void removeToken(Channel ch) {
        if (isLogin(ch)) {
            log.info("Channel logout {}, currentId: {}", ch.id(), getLoginId(ch));
            channelLoginTurn.remove(getLoginId(ch));
            channelLogin.remove(ch);
        } else {
            log.warn("No login for channel {}", ch.id());
        }
    }

    public ChannelFuture pushMessage(Long loginId, int type, String content) {
        return pushMessage(loginId, type, content.getBytes(StandardCharsets.UTF_8));
    }

    public ChannelFuture pushMessage(Long loginId, int type, byte[] body) {
        Channel ch = getChannel(loginId);
        if (ch == null) {
            log.warn("Can not find login id: {}", loginId);
            return null;
        }

        NetMsg netMsg = new NetMsg()
                .setCmdId(type)
                .body(body);

        return ch.writeAndFlush(netMsg);
    }
}
